package com.crm.miaohe.service;


import com.crm.miaohe.common.ApplicationConstant;
import com.crm.miaohe.config.K3CloudApiProperties;
import com.crm.miaohe.exception.ErpBusinessException;
import com.crm.miaohe.kdcloud.model.Customer;
import com.crm.miaohe.kdcloud.model.OrderForm;
import com.crm.miaohe.utils.MD5Utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kingdee.bos.webapi.sdk.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class K3CloudApiService {

    private static final int DEFAULT_BUFFER_SIZE = 4096;
    private final ObjectMapper objectMapper;
    private final K3CloudApiProperties properties;
    private final K3CloudApi k3CloudApi;
    private final ThreadLocal<String> threadCookieVal = new ThreadLocal<>();

    public K3CloudApiService(ObjectMapper objectMapper,
                             K3CloudApiProperties properties,
                             K3CloudApi k3CloudApi) {
        this.objectMapper = objectMapper;
        this.properties = properties;
        this.k3CloudApi = k3CloudApi;
    }

    protected HttpResponse execute(String serviceName, List<Object> params) throws IOException {
        HttpPost request = createRequest(serviceName, params);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            return httpClient.execute(request);
        }
    }

    protected <T> T execute(String serviceName, List<Object> params, ResponseHandler<T> responseHandler) throws IOException {
        HttpPost request = createRequest(serviceName, params);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            return httpClient.execute(request, responseHandler);
        }
    }

    public Boolean login() {
        return login(properties.getAccountId(), properties.getUsername(), properties.getPassword());
    }

    public Boolean login(String accountId, String username, String password) {
        return login(accountId, username, password, properties.getLanguage());
    }

    //登陆ERP
    private Boolean login(String accountId, String username, String password, Integer lang) {
        List<Object> params = List.of(accountId, username, password, lang);
        HttpResponse response;
        try {
            response = execute(properties.getLogin(), params);
        } catch (IOException e) {
            log.error("Error occurred when executing the request.", e);
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST,
                    ApplicationConstant.KINGDEE_005, e.getMessage());
        }
        final StatusLine statusLine = response.getStatusLine();
        final HttpEntity entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            try {
                EntityUtils.consume(entity);
            } catch (IOException e) {
                log.error("关闭响应数据流时发生异常。[{}]", e.getMessage(), e);
            }
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST,
                    ApplicationConstant.KINGDEE_005);
        }
        String responseStr = responseToString(response.getEntity());
        Map responseMap = jsonToMap(responseStr);
        Arrays.stream(response.getAllHeaders()).filter(header -> "Set-Cookie".equals(header.getName()))
                .filter(header -> {
                    String tempCookieVal = header.getValue();
                    return !ObjectUtils.isEmpty(tempCookieVal) && tempCookieVal.startsWith("kdservice-sessionid");
                })
                .findAny()
                .ifPresent(header -> threadCookieVal.set(header.getValue()));
        Integer loginResult = Objects.isNull(responseMap.get("LoginResultType")) ?
                null : (Integer) responseMap.get("LoginResultType");
        return !Objects.isNull(loginResult) && loginResult == 1;
    }

    public <T> T view(String formId, Map<String, Object> content, ResponseHandler<T> responseHandler) {
        if (login()) {
            List<Object> params = List.of(formId, content);
            try {
                //TODO 可能有问题
                return execute(properties.getView(), params, responseHandler);
                //return k3CloudApi.view(params, new OperateParam(content));
            } catch (IOException e) {
                log.error("查询数据时发生错误。", e);
                throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                        ApplicationConstant.KINGDEE_006, e.getMessage());
            }
        }
        return null;
    }

    //TODO
    public void submit(String formId, Map<String, Object> content) {
        List<Object> params = List.of(formId, content);
        try {
            execute(properties.getSubmit(), params);
        } catch (IOException e) {
            log.error("提交数据到金蝶时发生错误。", e);
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                    ApplicationConstant.KINGDEE_003, e.getMessage());
        }
    }

    //TODO
    public void audit(String formId, Map<String, Object> content) {
        List<Object> params = List.of(formId, content);
        try {
            execute(properties.getAudit(), params);
        } catch (IOException e) {
            log.error("审核数据时发生错误。", e);
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                    ApplicationConstant.KINGDEE_004, e.getMessage());
        }
    }

    public void unAudit(String formId, Map<String, Object> content) {
        List<Object> params = List.of(formId, content);
        try {
            execute(properties.getUnAudit(), params);
        } catch (IOException e) {
            log.error("审核数据时发生错误。", e);
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                    ApplicationConstant.KINGDEE_004, e.getMessage());
        }
    }

    //保存客户进ERP、然后提交、审核
    public Tuple2<String, String> saveCustomer(Customer customer) {
        String number = "";
        String id = "";
        try {
            SaveResult result = k3CloudApi.save("BD_Customer", new SaveParam<>(customer));
            Optional<SuccessEntity> var = result.getResult().getResponseStatus().getSuccessEntitys().stream().findFirst();
            if (var.isPresent()) {
                SuccessEntity entity = var.get();
                number = entity.getNumber();
                id = entity.getId();
            }
        } catch (Exception e) {
            log.error("保存客户数据到金蝶时发生异常。", e);
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                    ApplicationConstant.KINGDEE_002, e.getMessage());
        }
        if (login()) {
            Map<String, Object> params = Map.of("Numbers", number);
            try {
                // 提交客户信息
                submit("BD_Customer", params);
                // 审核客户信息
                audit("BD_Customer", params);

            } catch (Exception e) {
                log.error("提交、审核客户的时候发生异常。", e);

            }
        }
        return Tuples.of(number, id);
    }

    //保存订单到ERP
    public String saveOrder(OrderForm orderForm) {

        Map responseMap = save("SAL_SaleOrder", orderForm);
        return (String) ((Map) responseMap.get("Result")).get("Number");

        /*String number = "";
        String id = "";
        try {
            SaveResult result = k3CloudApi.save("SAL_SaleOrder", new SaveParam<>(orderForm));
            Optional<SuccessEntity> var = result.getResult().getResponseStatus().getSuccessEntitys().stream().findFirst();
            if (var.isPresent()) {
                SuccessEntity entity = var.get();
                number = entity.getNumber();
                id = entity.getId();
            }
        }catch (Exception e) {
            log.error("保存订单的时候发生异常。", e);
        }
        return number;*/
    }

    //TODO
    public <T> Map save(String formId, T t) {
        if (login()) {
            List<Object> params = List.of(formId, t);
            try {
                String response = execute(properties.getSave(), params, new BasicResponseHandler());
                if (log.isDebugEnabled()) {
                    log.debug(response);
                }
                Map responseMap = jsonToMap(response);
                if (Objects.nonNull(responseMap)) {
                    Map responseStatus = (Map) ((Map) responseMap.get("Result")).get("ResponseStatus");
                    if (!(Boolean) responseStatus.get("IsSuccess")) {
                        throw new ErpBusinessException(HttpStatus.BAD_REQUEST, ApplicationConstant.KINGDEE_006,
                                responseStatus.get("Errors"));
                    }
                } else {
                    throw new ErpBusinessException(HttpStatus.BAD_REQUEST, ApplicationConstant.KINGDEE_999);
                }
                return responseMap;
            } catch (IOException e) {
                log.error("创建订单时发生异常。", e);
                throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                        ApplicationConstant.KINGDEE_002, e.getMessage());
            }
        } else {
            log.error("登录金蝶系统时发生异常。请查询原因。");
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST,
                    ApplicationConstant.KINGDEE_005);
        }
    }

    //表单查询
    public <T> List<T> executeBillQuery(String formId, String fieldKeys, String filterString, Class<T> type) {
        QueryParam param = new QueryParam()
                .setFormId(formId).setFieldKeys(fieldKeys).setFilterString(filterString);
        try {
            return k3CloudApi.executeBillQuery(param, type);
        } catch (Exception e) {
            log.error("执行查询时发生异常。", e);
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                    ApplicationConstant.KINGDEE_006, e.getMessage());
        }
    }

    private HttpPost createRequest(String serviceName, List<Object> params) {
        HttpPost post = new HttpPost(URI.create(properties.getUrl().concat(serviceName)));
        buildHeader(post);
        initParameters(post, params);
        return post;
    }

    private HttpPost createRequest(String serviceName, String content) {
        HttpPost post = new HttpPost(URI.create(properties.getUrl().concat(serviceName)));
        buildHeader(post);
        initParameters(post, content);
        return post;
    }

    private void buildHeader(HttpPost request) {
        request.setHeader("Content-Type", "application/json");
        request.setHeader("AppVersion", "2.0");
        request.setHeader("User-Agent", "Kingdee/Java WebApi SDK 7.3 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        String clientId = "";
        String apiGwSec = "";
        String[] tmpArr = properties.getAppId().split("_");
        if (tmpArr.length == 2) {
            clientId = tmpArr[0];
            apiGwSec = tmpArr[1];
        }
        request.addHeader("X-Api-ClientID", clientId);
        request.addHeader("X-Api-Auth-Version", "2.0");
        String currentTimestamp = String.valueOf(Timestamp.valueOf(LocalDateTime.now()).getTime());
        request.addHeader("x-api-timestamp", currentTimestamp);
        request.addHeader("x-api-nonce", currentTimestamp);
        request.addHeader("x-api-signheaders", "X-Api-TimeStamp,X-Api-Nonce");
        String urlPath = URLEncoder.encode(request.getURI().toString(), StandardCharsets.UTF_8);
        String context = String.format("POST\n%s\n\nx-api-nonce:%s\nx-api-timestamp:%s\n", urlPath, currentTimestamp, currentTimestamp);
        request.addHeader("X-Api-Signature", MD5Utils.hashMAC(context, apiGwSec));
        request.addHeader("X-Kd-Appkey", properties.getAppId());
        request.addHeader("X-Kd-Signature", MD5Utils.hashMAC(properties.getAppId(), properties.getAppSec()));
        String cookieVal = threadCookieVal.get();
        if (!ObjectUtils.isEmpty(cookieVal)) {
            request.setHeader("Cookie", cookieVal);
        }
    }

    private void initParameters(HttpPost post, List<Object> obj) {
        Map<String, Object> params = new HashMap<>();
        params.put("format", "1");
        params.put("nonce", "");
        params.put("useragent", "ApiClient");
        params.put("rid", UUID.randomUUID().hashCode());
        /*try {
            String jsonString = objectMapper.writeValueAsString(obj);
            System.out.println(jsonString);
        } catch (IOException ignore) {

        }*/
        params.put("parameters", obj);
        params.put("timestamp", new Date().toString());
        params.put("version", "1.0");
        try {
            post.setEntity(new ByteArrayEntity(objectMapper.writeValueAsBytes(params),
                    ContentType.APPLICATION_JSON));
        } catch (JsonProcessingException e) {
            log.error("Failed to convert params to json format.");
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST,
                    ApplicationConstant.JSON_CONVERT_EX, e.getMessage());
        }
    }

    private void initParameters(HttpPost post, String content) {
        Map<String, Object> params = new HashMap<>();
        params.put("format", "1");
        params.put("nonce", "");
        params.put("useragent", "ApiClient");
        params.put("rid", UUID.randomUUID().hashCode());
        params.put("parameters", content);
        params.put("timestamp", new Date().toString());
        params.put("version", "1.0");
        try {
            post.setEntity(new ByteArrayEntity(objectMapper.writeValueAsBytes(params),
                    ContentType.APPLICATION_JSON));
        } catch (JsonProcessingException e) {
            log.error("Failed to convert params to json format.");
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST,
                    ApplicationConstant.JSON_CONVERT_EX, e.getMessage());
        }
    }

    private String responseToString(HttpEntity httpEntity) {
        int capacity = (int) httpEntity.getContentLength();
        if (capacity < 0) {
            capacity = DEFAULT_BUFFER_SIZE;
        }
        try (final InputStream inputStream = httpEntity.getContent();
             final Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
            final CharArrayBuffer buffer = new CharArrayBuffer(capacity);
            final char[] tmp = new char[1024];
            int l;
            while ((l = reader.read(tmp)) != -1) {
                buffer.append(tmp, 0, l);
            }
            return buffer.toString();
        } catch (IOException e) {
            log.error("Error occurred while handling response content.", e);
            return "{}";
        }
    }

    private Map jsonToMap(String json) {
        try {
            return objectMapper.readValue(json, Map.class);
        } catch (IOException e) {
            log.error("Failed to convert json to specified type [{}]", Map.class.getName());
            log.info(json);
            throw new ErpBusinessException(HttpStatus.BAD_REQUEST, e,
                    ApplicationConstant.JSON_CONVERT_EX, e.getMessage());
        }
    }
}
